#pragma once
#include "rtdev.h"
#include "rtdev/tio_sensor.h"
#include "rtdev/tio_common.h"
#include "rtdev/tio_gripper.h"

#include <list>

namespace rtdev {
class TIO : public RtDev
{
public:
    static const uint16_t TIO_CONNECT_WATCHDOG = 500;
    static const uint8_t MAX_RS485_CHANNEL = 2;
    static const uint8_t UIKEY_BIT_FREE_REQ = 0;
    static const uint8_t UIKEY_BIT_POINT_REQ = 1;
    static const uint8_t UIKEY_BIT_PAUSE_RESUME_REQ = 2;
    static const uint8_t UIKEY_BIT_CUSTOME = 3;
    static const uint8_t MAX_RS485_FRAME_SIZE = 8;
    static const uint8_t MAX_RS485_BUFF = MAX_RS485_FRAME_SIZE * 8;
    typedef enum
    {
        RS485_RTU = 0,
        RS485_TRANS = 1,
        RS485_SENSOR = 2,
    } RS485Mode;
    typedef struct
    {
        int rs485_sensor_chn;                   // 力传感器所在的通道
        int rs485_mode_Cfg[MAX_RS485_CHANNEL];  // 485通道的模式，0 for RTU, 1 for 透传， 2 for 传感器
        int rs485_chn_ena[MAX_RS485_CHANNEL];   // flag for channel enable or not， 由TIO报文决定
    } RS485Setting;
    // 接受TIO485数据缓存
    typedef struct
    {
        uint16_t data_len;
        bool is_frame_part_in[MAX_RS485_FRAME_SIZE];
        uint8_t tio_rs485_buf[MAX_RS485_BUFF];  // buffer the splitted frame
    } RS485RTUMsg;

public:
    TIO();
    virtual ~TIO();
    int boot_init();
    void shutdown();
    virtual void update();
    virtual void action_when_online();
    void reset_tio();  // 上电后重置buffer
    void comm_write_TIO_Setting(TIOSetting* data) { *data = tio_setting_; };
    void comm_read_TIO_key(uint8_t data);
    void comm_read_TIO_DI(uint8_t data) { tio_status_.DI_buffer = data; }
    void comm_read_TIO_DO(uint8_t data) { tio_status_.DO_buffer = data; }
    void comm_read_TIO_RGB(uint8_t data);
    void comm_read_TIO_AI1(uint16_t data);
    void comm_read_TIO_AI2(uint16_t data);
    int comm_write_RS485_chn(uint8_t chn, uint8_t* data);

    void comm_read_RS485_chn(uint8_t chn, uint8_t* data, uint8_t len);
    void comm_read_RS485_inner_sensor(uint8_t* data, uint8_t len);

private:
    void comm_jkcan_map();
    void comm_jkcan_tx_map(int busid);

private:
    bool collect_frame(uint8_t chn, uint8_t* data, uint8_t len);
    int proc_Rs485_response_data(uint8_t chn, uint8_t* buf, int len);
    void check_tio_cmd_timeout();
    void set_gripper_error(GripperStateUnit* gripper_sig, int type);

    TIOSensor tio_sensor_;
    RS485Setting rs485_setting_ = {};
    TIOSetting tio_setting_ = {};
    TIOStatus tio_status_ = {};
    RS485RTUMsg tio_rs485_RTU_msg_[3] = {};  // 0x720, 0x730, 0x740 数据缓存
    uint16_t hw_version_ = 0;
    uint16_t sw_version_ = 0;

public:
    uint32_t get_tio_version() const { return hw_version_ << 16 + sw_version_; };
    TIOSetting get_tio_settings() const { return tio_setting_; }
    TIOStatus get_tio_status() const { return tio_status_; }
    int is_Rs485_chn_enable(int chn) const { return rs485_setting_.rs485_chn_ena[chn]; }
    int get_Rs485_chn_mode(int chn) const { return rs485_setting_.rs485_mode_Cfg[chn]; }

    int tio_set_dout(uint8_t index, uint8_t dout);
    int tio_set_lamp(TIOLampColor color);
    /**
     * @brief 配置RS485接口数据传输模式
     * 
     * @param chn 0:RS485 channel 1, 1:RS485 channel 2
     * @param mode 0:Modbus RTU模式, 1:数据转发模式
     * 
     * @return int 0 for success, -1 for failure
     */
    int set_tio_rs485_chn_mode(int chn, RS485Mode mode);
    /**
     * @brief 配置通讯参数
     * 
     * @param chn RS485接口ID, 0:RS485 channel 1, 1:RS485 channel 2
     * @param baudrate 波特率, 无符号整数，最大2250000
     * @param databit 数据位, 0:8位数据位, 1:9位数据位
     * @param stopbit 停止位, 0:1个停止位, 1:0.5个停止位, 2:2个停止位, 3:1.5个停止位
     * @param parity 校验位, 0:无校验, 1:偶校验, 2:奇校验
     * @return int 0 for success, -1 for failure
     */
    int set_tio_rs485_chn_comm(int chn, int baudrate, int databit, int stopbit, char parity);
    /**
     * @brief 设置RS485通道功能
     * 
     * @param cfg 通道功能, 0:不使用力矩传感器; 
     * 					   1:力矩传感器接RS485_1接口，此时报文ID使用0x730和0x731
     *                     2:力矩传感器接RS485_2接口，此时报文ID使用0x720和0x721
     * @return int 0 for success, -1 for failure
     */
    int tio_set_rs485_chn_func(uint32_t cfg);
    // 0:0x00 DI2输入类型为NPN,DI1输入类型为NPN
    // 1:0x01 DI2输入类型为NPN,DI1输入类型为PNP
    // 2:0x10 DI2输入类型为PNP,DI1输入类型为NPN
    // 3:0x11 DI2输入类型为PNP,DI1输入类型为PNP
    int tio_setup_di_pin(uint32_t cfg);
    // 低8位数据有效，低8位数据中，高4位为DO2配置，低四位为DO1配置，具体含义如下：
    // 0x0 DO输出为类型为NPN输出
    // 0x1 DO输出类型为PNP输出
    // 0x2 DO输出类型为推挽输出
    // 0xF RS485H接口使能，DO禁止
    int tio_setup_do_pin(uint32_t cfg);
    //  0:模拟输入功能使能，RS485L禁止
    // 1:RS485L接口使能，模拟输入功能禁止
    int tio_setup_ai_pin(uint32_t cfg);
    int tio_set_vout_enable(uint32_t cfg);
    int tio_set_vout_voltage(uint32_t data);
    // 0 for 24V, 1 for 12V
    int query_Tio_version();

public:
    std::list<uint8_t> rs485_trans_resp_msg_[MAX_RS485_CHANNEL];  // 透传返回数据
    std::list<uint8_t> rs485_rtu_cmd_msg_[MAX_RS485_CHANNEL];     // rtu 指令队列
    int get_rs485_responce_msg(int chn, int len, uint8_t* msg);

private:
    static const uint16_t OBJID_VERSION = 0x0001;
    static const uint16_t OBJID_OUT_VOLTAGE = 0x100;
    static const uint16_t OBJID_DI_CFG = 0x101;
    static const uint16_t OBJID_DO_CFG = 0x102;
    static const uint16_t OBJID_AI_CFG = 0x103;
    static const uint16_t OBJID_RS4852_MOD = 0x104;
    static const uint16_t OBJID_RS4851_MOD = 0x105;
    static const uint16_t OBJID_RS4852_COMM = 0x106;
    static const uint16_t OBJID_RS4851_COMM = 0x107;
    static const uint16_t OBJID_SENSOR_CHN_CFG = 0x10A;
    static const uint16_t OBJID_VOUT_ENABLE = 0x301;

    // 未使用
    static const uint16_t OBJID_HEARTBEAT = 0x207;
    static const uint16_t OBJID_CURRENT = 0x208;
    static const uint16_t OBJID_VOLTAGE = 0x209;
    static const uint16_t OBJID_TEMPERATURE = 0x20A;

    int get_sdo(uint32_t obj_id, std::function<int(uint8_t*, int)> cb, int timeout, int retry);
    int set_sdo(uint32_t obj_id, uint8_t* val, int size, std::function<int(uint8_t*, int)> cb, int timeout, int retry);
};

TIO* get_TIO();
}  // namespace rtdev
